#include "logger.hpp"
#include "typeHelpers.hpp"

namespace hms::log
{

std::shared_ptr<spdlog::logger> createLogger( const Settings& settings ){
	using size_type = typename std::vector<spdlog::sink_ptr>::size_type;
	auto nSinks {static_cast<size_type>(1 + settings.useLogFile)};
	
	std::vector<spdlog::sink_ptr> sinks (nSinks);

	auto sink = [&](Sink s) -> spdlog::sink_ptr& {
		return sinks[static_cast<size_type>(s)];
	};

	/* console sink */
	sink(Sink::console) = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
	sink(Sink::console)->set_level  ( settings.logLevel_console );
	sink(Sink::console)->set_pattern( settings.customLogFormat_console );

	/* file sink */
	if ( settings.useLogFile ){
		fs::path logfile { settings.logDirectory / settings.logFileName };
		if ( fs::is_regular_file(logfile) && !settings.appendLog )
			fs::remove(logfile);
		if ( settings.useLogFileRotation ){
			sink(Sink::file) =
				std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
					logfile,
					settings.logRotationMaxSizeKB * 1024,
					settings.logRotationMaxFiles
				);
		} else {
			sink(Sink::file) =
				std::make_shared<spdlog::sinks::basic_file_sink_mt>(logfile);
		}
		sink(Sink::file)->set_level  ( settings.logLevel_file );
		sink(Sink::file)->set_pattern( settings.customLogFormat_file );
	}
	std::shared_ptr<spdlog::logger> logger;
	if (settings.useAsyncLogger){
		logger = std::make_shared<spdlog::async_logger>(
			log::loggerName, sinks.begin(), sinks.end(),
			spdlog::thread_pool(), spdlog::async_overflow_policy::block
		);
	} else {
		logger = std::make_shared<spdlog::logger>(
			log::loggerName, sinks.begin(), sinks.end()
		);
	}
	/* apparently, global logger level affects sinks */
	logger->set_level(Level::trace);
	spdlog::register_logger(logger);
	spdlog::set_default_logger(logger);
	return logger;
}


std::shared_ptr<logger> getLogger(){
	std::shared_ptr<logger> logger { spdlog::get(hms::log::loggerName) };
	assert( logger );
	return logger;
}

void setLevel( std::shared_ptr<spdlog::logger>& logger, Sink sink, Level level){
	assert( logger );
	using T = typename remove_qualifiers<decltype(logger->sinks())>::size_type;
	logger->sinks()[static_cast<T>(sink)]->set_level(level);
}

void setLevel(Sink sink, Level level){
	if (auto logger {getLogger()} ){
		setLevel(logger, sink, level);
	}
}

} // namespace hms::logger
